package kunsch.bexp.transform;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import kunsch.bexp.BooleanFunction;
import kunsch.bexp.Expression;
import kunsch.bexp.Gate;
import kunsch.bexp.Not;
import kunsch.bexp.Variable;

public class WeightHeuristicVariableOrderStrategy implements VariableOrderStrategy
{
	BooleanFunction function;
	Map<String, Double> variableToWeight = new HashMap<String, Double>();
	Double currentWeight = 1.0;

	public WeightHeuristicVariableOrderStrategy(BooleanFunction function)
	{
		this.function = function;
	}

	@Override
	public List<Variable> order(List<Variable> variables)
	{
		computeVariableWeight(function.getIn());
		List<Variable> orderedVariables = computeOrderByWeight(variables); 		
		
		return orderedVariables;
	}

	private List<Variable> computeOrderByWeight(List<Variable> variables)
	{
		List<Variable> ordered = new ArrayList<Variable>();
		
		while (variableToWeight.keySet().size() != 0)
		{
			double min = 0;
			String toDelete = "";
			for (String varString : variableToWeight.keySet())
			{
				double weight = variableToWeight.get(varString);
				if (weight > min)
				{
					min = weight;
					toDelete = varString;
				}
			}
			
			for(Variable var : variables)
			{
				if(var.getDescription().equals(toDelete))
					ordered.add(var);
			}
			
			variableToWeight.remove(toDelete);
		}
		return ordered;
	}

	private void computeVariableWeight(Expression expr)
	{
		if (expr instanceof Gate)
		{
			Gate container = (Gate) expr;
			currentWeight *= 1 / (double) container.getExpressions().size();
			for (Expression actExp : container.getExpressions())
			{
				computeVariableWeight(actExp);
			}
			currentWeight *= container.getExpressions().size();
		}
		else if (expr instanceof Not)
		{
			computeVariableWeight(((Not) expr).getExp());
		}
		else if (expr instanceof Variable)
		{
			Double oldWeight = 0.0;
			if (variableToWeight.containsKey(((Variable) expr).getDescription()))
			{
				oldWeight = variableToWeight.get(((Variable) expr).getDescription());
			}
			variableToWeight.put(((Variable) expr).getDescription(), currentWeight + oldWeight);
		}

	}
}
